#include "enum_and_data.h"
#include "show.h"
#include "calcu.h"
using namespace std;

enum MES get_input(array<bool, detect_num>& keyStatusLast, array<bool, detect_num>& keyStatusNow);
bool exam_param();

int main()
{
    //初始化全局变量
    //状态变量
    bool flash_enable = 0;              //下划线光标闪烁使能
    enum PAGE current_page = WELCOME;   //当前页面，用于控制状态转移
    enum PAGE last_page = UNKNOWN;      //上个页面，用于实现ASSIST页面的类回溯
    enum SELECT select_status = FINISH; //选择状态，用于控制SEARCH和BUY_SELECT的子状态转移
    //键盘输入信息及缓冲区
    enum MES input = NONE;                  //键盘输入信息
    array<bool, detect_num> keyStatusLast;  //上个按键状态
    array<bool, detect_num> keyStatusNow;   //当前按键状态，用于检测一个更新周期内键盘按下或抬起
    keyStatusLast.fill(0);
    keyStatusNow.fill(0);
    //状态间传递变量
    int counter = 0;                    //倒计时计数器
    int flash = 0;                      //下划线光标闪烁计数器
    int temp = 0;                       //纯粹是习惯上把寄存变量弄个内存池，虽然并没有占什么内存
    int write_i = 0;                    //记录输入框输入到的位置，用于实现下划线光标闪烁，否则直接把数字打出来就好了

    int search_station_num = 0;         //被查询的站点的编号
    int start_station_num = 0;          //起点的编号
    int end_station_num = 0;            //终点的编号
    int station_pass = 0;               //计算得到的最少经过段数
    int single_price = 0;               //由station_pass计算得到的车票单价
    int ticket_number_num = 0;          //购买的票数
    int sum_price = 0;                  //需要支付的总金额
    int put_price = 0;                  //已经投币投入的总金额

    if(exam_param()){                   //检查可变参数是否合法
        goto TERMINAL;                  //虽然很不想用goto，但是控制循环运行的run变量一直不生效
    }                                   //而且初始化之前不允许先根据配置goto是C语言遗留下来的标准吗

    init_gragh();
    show_image(0, 0, static_cast<int>(win_width/2), win_height, wel_path);
    show_image(win_width * 0.5, 0.0, static_cast<int>(win_width/2), win_height, info_path);

    while(1){
        //为了稳定刷新率，使用计时来稳定延迟
        auto start_t = std::chrono::steady_clock::now();

        input = get_input(keyStatusLast, keyStatusNow);
        switch (current_page){
        case UNKNOWN:                   //单纯需要一个空页面好处理初始状态
            break;

        case WELCOME:
            last_page = WELCOME;
            if (input == ENTER){
                current_page = MENU;
                cleardevice();
                show_image(0, 0, static_cast<int>(win_width/2), win_height, menu_path);
                show_image(win_width * 0.5, 0.0, static_cast<int>(win_width/2), win_height, instruct_path);
            }
            break;

        case MENU:
            last_page = MENU;
            switch (input){
            case NONE:
                break;
            case EXIT:
                closegraph();
                goto TERMINAL;
                break;

            case A:
                current_page = SEARCH;
                select_status = FIND;
                search_station_num = 0;
                write_i = 6;
                flash_enable = 1;
                cleardevice();
                show_image(0, 0, static_cast<int>(win_width/2), win_height, frame_path);
                show_image(18, 22, static_cast<int>(win_width/2.1), static_cast<int>(win_height/1.05), map_path);
                show_image(static_cast<int>(win_width/2), 0, static_cast<int>(win_width/2), win_height, search_path);
                show_word_search();
                break;
            case B:
                current_page = BUY_SELECT;
                select_status = START;
                start_station_num = 0;
                end_station_num = 0;
                ticket_number_num = 0;
                station_pass = 0;
                single_price = 0;
                write_i = 0;
                flash_enable = 1;
                cleardevice();
                show_image(0, 0, static_cast<int>(win_width/2), win_height, frame_path);
                show_image(18, 22, static_cast<int>(win_width/2.1), static_cast<int>(win_height/1.05), map_path);
                show_image(static_cast<int>(win_width/2), 0, static_cast<int>(win_width/2), win_height, buy_path);
                show_word_buy();
                break;
            default:
                break;
            }
            break;

        case SEARCH:
            last_page = SEARCH;
            switch (input){
            case NONE:
                break;
            case HELP:
                current_page = ASSIST;
                clean_left();
                show_image(0, 0, static_cast<int>(win_width/2), win_height, instruct_path);
                break;
            case EXIT:
                current_page = MENU;
                flash_enable = 0;
                cleardevice();
                show_image(0, 0, static_cast<int>(win_width/2), win_height, menu_path);
                show_image(static_cast<int>(win_width/2), 0, static_cast<int>(win_width/2), win_height, info_path);
                break;

            case A:
                current_page = BUY_SELECT;
                select_status = START;
                start_station_num = 0;
                end_station_num = 0;
                ticket_number_num = 0;
                station_pass = 0;
                single_price = 0;
                write_i = 0;
                flash_enable = 1;
                clean_right();
                show_image(18, 22, static_cast<int>(win_width/2.1), static_cast<int>(win_height/1.05), map_path);
                show_image(static_cast<int>(win_width/2), 0, static_cast<int>(win_width/2), win_height, buy_path);
                show_word_buy();
                break;
            case B:
            case C:
                break;

            case ENTER:
                clean_wrong(FIND);
                if (search_station_num == 0){
                    show_wrong(FIND, warning_zero);
                }else{
                    select_status = FINISH;
                    show_station(station_picture[search_station_num-1], FIND);
                    flash_enable = 0;
                }
                break;
            //下面两个case要考虑两位和一位的问题，以后拓展到多位就是考虑刚好满和全没有，要用到log()
            case BACK:
                clean_wrong(FIND);
                if (select_status == FINISH){
                    select_status = FIND;
                    show_image(18, 22, static_cast<int>(win_width/2.1), static_cast<int>(win_height/1.05), map_path);
                    flash_enable = 1;
                }
                if (search_station_num != 0){
                    chart_back(write_i);
                    if (search_station_num >= pow(10, input_chart_deep - 1)){
                        flash_enable = 1;
                    }
                    search_station_num /= 10;
                }
                break;
            default:
                clean_wrong(FIND);
                if (search_station_num != 0 || input != ZERO){
                    temp = search_station_num * 10 + static_cast<int>(input);
                    if (temp <= max_station_number){
                        chart_push(write_i, static_cast<int>(input));
                        search_station_num = temp;
                        if(search_station_num >= pow(10, input_chart_deep - 1)){
                            flash_enable = 0;
                        }
                    } else {
                        show_wrong(FIND, warning_over);
                        if (temp >= pow(10, input_chart_deep)){
                            chart_back(write_i);
                            flash_enable = 1;
                        }
                        chart_back(write_i);
                        search_station_num = 0;
                    }
                }
            }
            flash_line(flash_enable, flash, write_i);
            break;

        case BUY_SELECT:
            last_page = BUY_SELECT;
            switch (input){
            case NONE:
                break;
            case HELP:
                current_page = ASSIST;
                clean_left();
                show_image(0, 0, static_cast<int>(win_width/2), win_height, instruct_path);
                break;
            case EXIT:
                current_page = MENU;
                flash_enable = 0;
                cleardevice();
                show_image(0, 0, static_cast<int>(win_width/2), win_height, menu_path);
                show_image(win_width * 0.5, 0.0, win_width/2, win_height, info_path);
                break;
            case A:
            case B:
            case C:
                break;

            case ENTER:
                switch (select_status){
                case START:
                    if (start_station_num == 0){
                        clean_wrong(START);
                        show_wrong(START, warning_zero);
                    } else {
                        select_status = END;
                        show_station(station_picture[start_station_num-1], START);
                        //分开处理输入框未满和已满两种情况的遗留下划线
                        if (start_station_num < pow(10, input_chart_deep - 1)){
                            chart_push(write_i, INT_MIN);
                        } else {
                            flash_enable = 1;
                        }
                        //下划线无论如何都移动到下一个输入框
                        write_i = 2;
                    }
                    break;
                case END:   //复制粘贴，但是整合成一个模块或者类又有十多个参数、五六个变量
                    if (end_station_num == 0){
                        clean_wrong(END);
                        show_wrong(END, warning_zero);
                    } else {
                        select_status = TICKET;
                        station_pass = arrange_route(start_station_num, end_station_num);
                        single_price = calcu_price(station_pass);
                        show_station(station_picture[end_station_num-1], END);
                        if (end_station_num < pow(10, input_chart_deep - 1)){
                            chart_push(write_i, INT_MIN);
                        } else {
                            flash_enable = 1;
                        }
                        show_single_price(single_price);
                        write_i = 4;
                    }
                    break;
                case TICKET:
                    ticket_number_num = max(1, ticket_number_num);
                    select_status = FINISH;
                    sum_price = single_price * ticket_number_num;
                    put_price = 0;
                    clean_right();
                    flash_enable = 0;
                    current_page = BUY_PAY;
                    show_image(0, 0, static_cast<int>(win_width/2), win_height, pay_path);
                    show_pay(sum_price, put_price);
                    break;
                default:
                    break;
                }
                break;
            case BACK:
                switch (select_status){
                case START:
                    clean_wrong(START);
                    if (start_station_num != 0){
                        chart_back(write_i);
                        if (start_station_num >= pow(10, input_chart_deep - 1)){
                            flash_enable = 1;
                        }
                        start_station_num /= 10;
                    }
                    break;
                case END:
                    clean_wrong(END);
                    if (end_station_num != 0){
                        chart_back(write_i);
                        if (end_station_num >= pow(10, input_chart_deep - 1)){
                            flash_enable = 1;
                        }
                        end_station_num /= 10;
                    }
                    break;
                case TICKET:
                    clean_wrong(TICKET);
                    if (ticket_number_num != 0){
                        chart_back(write_i);
                        if (ticket_number_num >= pow(10, input_chart_deep - 1)){
                            flash_enable = 1;
                        }
                        ticket_number_num /= 10;
                    }
                    break;
                default:
                    break;
                }
                break;
            default:
                switch (select_status){
                case START:
                    clean_wrong(START);
                    if (start_station_num != 0 || input != ZERO){
                        temp = start_station_num * 10 + static_cast<int>(input);
                        if (temp <= max_station_number){
                            chart_push(write_i, static_cast<int>(input));
                            start_station_num = temp;
                            if (start_station_num >= pow(10, input_chart_deep - 1)){
                                flash_enable = 0;
                            }
                        } else {
                            show_wrong(START, warning_over);
                            if (temp >= pow(10, input_chart_deep)){
                                chart_back(write_i);
                                flash_enable = 1;
                            }
                            chart_back(write_i);
                            start_station_num = 0;
                        }
                    }
                    break;
                case END:
                    clean_wrong(END);
                    if (end_station_num != 0 || input != ZERO){
                        temp = end_station_num * 10 + static_cast<int>(input);
                        if (temp <= max_station_number){
                            chart_push(write_i, static_cast<int>(input));
                            end_station_num = temp;
                            if (end_station_num >= pow(10, input_chart_deep - 1)){
                                flash_enable = 0;
                            }
                        } else {
                            show_wrong(END, warning_over);
                            if (temp >= pow(10, input_chart_deep)){
                                chart_back(write_i);
                                flash_enable = 1;
                            }
                            chart_back(write_i);
                            end_station_num = 0;
                        }
                    }
                    break;
                case TICKET:
                    clean_wrong(TICKET);
                    if (ticket_number_num != 0 || input != ZERO){
                        temp = ticket_number_num * 10 + static_cast<int>(input);
                        if (temp <= max_ticket){
                            chart_push(write_i, static_cast<int>(input));
                            ticket_number_num = temp;
                            if (ticket_number_num >= pow(10, input_chart_deep - 1)){
                                flash_enable = 0;
                            }
                        } else {
                            show_wrong(TICKET, warning_over);
                            if (temp >= pow(10, input_chart_deep)){
                                chart_back(write_i);
                                flash_enable = 1;
                            }
                            chart_back(write_i);
                            ticket_number_num = 0;
                        }
                    }
                    break;
                default:
                    break;
                }
            }
            flash_line(flash_enable, flash, write_i);
            break;

        case BUY_PAY:
            last_page = BUY_PAY;
            switch (input){
            case NONE:
                break;
            case HELP:
                current_page = ASSIST;
                clean_left();
                show_image(0, 0, static_cast<int>(win_width/2), win_height, instruct_path);
                break;
            case EXIT:
                counter = wait_time_giveback / refresh_cycle;
                current_page = GIVEBACK;
                cleardevice();
                show_image(0, 0, static_cast<int>(win_width/2), win_height, giveback_path);
                show_giveback(cvrt_price_to_cash(put_price));
                break;

            case A:
                put_price += 10;
                break;
            case B:
                put_price += 5;
                break;
            case C:
                put_price += 1;
                break;
            default:
                break;
            }
            if (put_price >= sum_price){
                counter = wait_time_finish / refresh_cycle;
                current_page = BUY_FINISH;
                cleardevice();
                show_image(win_width * 0.5, 0, static_cast<int>(win_width/2), win_height, finish_path);
                show_finish(cvrt_price_to_cash(put_price - sum_price), ticket_number_num);
            } else {
                clean_right();
                show_pay(sum_price, put_price);
            }
            break;

        case BUY_FINISH:
            if (input == ENTER || counter < 0){
                current_page = WELCOME;
                cleardevice();
                show_image(0, 0, static_cast<int>(win_width/2), win_height, wel_path);
                show_image(win_width * 0.5, 0, win_width/2, win_height, info_path);
            } else {
                counter--;
            }
            break;

        case GIVEBACK:
            if (input == ENTER||counter < 0){
                current_page = MENU;
                cleardevice();
                show_image(0, 0, static_cast<int>(win_width/2), win_height, menu_path);
                show_image(win_width * 0.5, 0 , static_cast<int>(win_width/2), win_height, instruct_path);
            } else {
                counter--;
            }
            break;

        case ASSIST:
            switch (last_page){
            case SEARCH:
                clean_left();
                show_image(0, 0, static_cast<int>(win_width/2), win_height, menu_path);
                show_image(win_width * 0.5, 0 , static_cast<int>(win_width/2), win_height, instruct_path);
                if (select_status == FINISH) {
                    show_station(station_picture[search_station_num-1], FIND);
                }
                current_page = SEARCH;
                break;
            case BUY_SELECT:
                clean_left();
                show_image(0, 0, static_cast<int>(win_width/2), win_height, frame_path);
                show_image(18, 22, static_cast<int>(win_width/2.1), static_cast<int>(win_height/1.05), map_path);
                if (select_status == TICKET) {
                    show_station(station_picture[start_station_num-1], START);
                    show_station(station_picture[end_station_num-1], END);
                } else if (select_status == END){
                    show_station(station_picture[start_station_num-1], START);
                }
                current_page = BUY_SELECT;
                break;
            case BUY_PAY:
                clean_left();
                show_image(0, 0, static_cast<int>(win_width/2), win_height, frame_path);
                show_image(18, 22, static_cast<int>(win_width/2.1), static_cast<int>(win_height/1.05), map_path);
                show_station(station_picture[start_station_num-1], START);
                show_station(station_picture[end_station_num-1], END);
                current_page = BUY_FINISH;
                break;
            default:
                break;
            }
        default:
            break;
        }

        //停止计时，结算运算用时，补足延迟实现低配版垂直同步
        auto end_t = std::chrono::steady_clock::now();
        auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(end_t - start_t);
        int dura_t = duration.count();
        if (dura_t <= refresh_cycle){
            Sleep(refresh_cycle - dura_t);
        }
    }

TERMINAL:
    return 0;
}

enum MES get_input(array<bool, detect_num>& keyStatusLast, array<bool, detect_num>& keyStatusNow)
{
    int i = 0;
    MES input = NONE;
    bitset<detect_num> keyDown;     //实际上17和24没什么区别
    for (i = 0;i < detect_num;i++){
        keyStatusNow.at(i) = ((GetAsyncKeyState(keyUnderDetect.at(i)) & 0x8000) != 0);
        keyDown[i] = (!keyStatusLast.at(i) && keyStatusNow.at(i));
        keyStatusLast.at(i) = keyStatusNow.at(i);
    }

    //如果存在同时按下（概率很小），则输出NONE
    if (keyDown.count() == 1){
        for (i = 0;i < detect_num;i++){
            if (keyDown.test(i) == 1){
                input = static_cast<enum MES>(i);
            }
        }
    }
    return input;
}

bool exam_param()
{
    bool illegal = 0;   //放弃完整的SISO，正规写法是写异常，太麻烦了
    if (refresh_cycle <= 0){
        cout << "错误：光标闪烁周期不能为非正数；\n\n";
        illegal = 1;
    }
    if (max_ticket < 0 || max_ticket >= pow(10, input_chart_deep)){
        cout << "错误：票数上限不能为负数或超过" << input_chart_deep << "位；\n\n";
        illegal = 1;
    }
    if (illegal){
       return illegal;
    }

    if (flash_cycle % refresh_cycle != 0){
        cout << "警告：光标闪烁周期未设置为帧更新周期的整数倍；\n\n";
    }
    if (wait_time_finish % refresh_cycle != 0){
        cout << "警告：结束等待时间未设置为帧更新周期的整数倍；\n\n";
    }
    if (wait_time_giveback % refresh_cycle != 0){
        cout << "警告：退币等待时间未设置为帧更新周期的整数倍；\n\n";
    }
    return illegal;
}
